package utilfuncs

import (
	"fmt"
	"math"
	"time"
)

var (
	now            = time.Now()
	todayEpochTime = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
)

func MapKeys[TK comparable, TV any](m map[TK]TV) []TK {
	keys := []TK{}
	for k, _ := range m {
		keys = append(keys, k)
	}
	return keys
}

func FilterMapByKeys[TK comparable, TV any](m map[TK]TV, validKeys []TK) map[TK]TV {
	returnMap := map[TK]TV{}
	for _, k := range validKeys {
		if v, exists := m[k]; exists {
			returnMap[k] = v
		}
	}
	return returnMap
}

func MaxInt(a, b uint) uint {
	if a > b {
		return a
	} else {
		return b
	}
}

func ArrayWithValue(size int, s string) []string {
	r := make([]string, size)
	for i := range r {
		r[i] = s
	}
	return r
}

func ArrayIntToArryString(strings []int) []string {
	r := []string{}
	for _, i := range strings {
		r = append(r, fmt.Sprintf("%d", i))
	}
	return r
}

func Anyify[T any](items []T) []any {
	r := []any{}
	for _, s := range items {
		r = append(r, any(s))
	}
	return r
}

func MapValueAdd(m map[string]uint, value uint) map[string]uint {
	r := map[string]uint{}
	for k, v := range m {
		r[k] = v + value
	}
	return r
}

func StringInArray(items []string, findItem string) bool {
	for _, item := range items {
		if item == findItem {
			return true
		}
	}
	return false
}

func Flatten[T any](arrArrs [][]T) []T {
	r := []T{}
	for _, arr := range arrArrs {
		for _, a := range arr {
			r = append(r, a)
		}
	}
	return r
}

func Pairs[T any](items []T) [][]T {
	pairs := [][]T{}
	for _, s1 := range items {
		for _, s2 := range items {
			pairs = append(pairs, []T{s1, s2})
		}
	}
	return pairs
}

func IntArraysSortedFilter(intArrays [][]int) [][]int {
	r := [][]int{}
OUTER:
	for _, items := range intArrays {
		last := -99999
		for _, item := range items {
			if item < last {
				continue OUTER
			} else {
				last = item
			}
		}
		r = append(r, items)
	}
	return r
}

func DistHaversine(a, b Coord) float64 {
	phiA := (90.0 - a.Lat) * math.Pi / 180.0
	phiB := (90.0 - b.Lat) * math.Pi / 180.0
	thetaA := a.Lon * math.Pi / 180.0
	thetaB := b.Lon * math.Pi / 180.0
	c := math.Sin(phiA)*math.Sin(phiB)*math.Cos(thetaA-thetaB) + math.Cos(phiA)*math.Cos(phiB)
	arc := math.Acos(c)
	scaleKm := 6371.0
	return arc * scaleKm
}

func DurationFormatSecs(seconds uint) string {
	if seconds < 60 {
		return fmt.Sprintf("%ds", seconds)
	} else {
		minutes := seconds / 60
		secsrem := seconds % 60
		return fmt.Sprintf("%dm %ds", minutes, secsrem)
	}
}

// TODO: is this better to do via time package (AM/PM formatting free then?)
func TimeHMSFormatSecs(seconds uint) string {
	var (
		s = seconds % 60
		m = (seconds / 60) % 60
		h = seconds / 3600
	)

	if s == 0 {
		return fmt.Sprintf("%02d:%02d", h, m)
	} else {
		return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
	}
}

func DayYYYYMMDD(t *time.Time) uint {
	y, m, d := t.Date()
	return uint(y*10000) + uint((m * 100)) + uint(d)
}

func DaySeconds(t *time.Time) uint {
	y, m, d := t.Date()
	midnight := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
	return uint(t.Sub(midnight).Seconds())
}

func ItemInArray[T comparable](item T, items []T) bool {
	for _, s := range items {
		if item == s {
			return true
		}
	}
	return false
}
